//  This file is part of Swordbible - http://www.swordwarrior.net
//  Copyright (C) 2003-2008 Manu Mital mital.manu@gmail.com

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2, or (at your option)
//  any later version.

//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software Foundation,
//  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


// BottomLeft.cpp : implementation file
//

#include "stdafx.h"
#include "SwordBible.h"
#include "SwordBibleDoc.h"
#include "BottomLeft.h"
#include ".\bottomleft.h"
#include "ColorMgr.h"
#include "BibleForm.h"



CString CBottomLeft::ActiveMainFrameCommand;
CSwordBibleFonts CBottomLeft::font;
// to fill in the alphabet combo box
CString CBottomLeft::abc[26] = {"A", "B", "C", "D", "E", "F", "G", "H","I","J","K","L","M","N",
"O","P","Q","R","S","T","U","V","W","X","Y","Z"};

int CBottomLeft::active = -1;
// CBottomLeft dialog


IMPLEMENT_DYNAMIC(CBottomLeft, CPropertyPage)
CBottomLeft::CBottomLeft()
	: CPropertyPage(CBottomLeft::IDD)
{
	
	pModule = NULL;
	
}

CBottomLeft::~CBottomLeft()
{
	

}

void CBottomLeft::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);

	DDX_Control(pDX, IDC_RICHEDIT_BOTTOMLEFT, m_lxRich);
	DDX_Control(pDX, IDC_LIST_ENTRIES, EntriesList);
	DDX_Control(pDX, IDC_SEARCH_DICT, SearchEdit);
	
}


BEGIN_MESSAGE_MAP(CBottomLeft, CPropertyPage)
	ON_WM_SIZE()
	ON_COMMAND(ID_BOTTOMLEFT_SELECTALL, OnSelectAll)
	ON_COMMAND(ID_BOTTOMLEFT_COPY, OnBottomleftCopy)
	ON_COMMAND(ID_BOTTOMLEFT_FONT, OnBottomleftFont)
	ON_COMMAND(ID_BOTTOMLEFT_PRINT,OnPrint)
	
	ON_COMMAND(ID_BOTTOMLEFT_ZOOM, OnZoom)
	ON_UPDATE_COMMAND_UI(ID_BOTTOMLEFT_ZOOM, OnUpdateZoom)

	ON_COMMAND(ID_BOTTOMLEFT_NEXT,OnNext)
	ON_COMMAND(ID_BOTTOMLEFT_PREVIOUS,OnPrevious)

	ON_COMMAND(ID_DICTIONARY_VERSIONS, OnShowVersions)	


	ON_MESSAGE(ID_DICTIONARY_COMMAND, OnMainFrameCommand)

   ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
   ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
   ON_NOTIFY(LVN_GETDISPINFO, IDC_LIST_ENTRIES, &CBottomLeft::GetDispInfo)
   ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_ENTRIES, &CBottomLeft::OnItemChanged)
  ON_EN_CHANGE(IDC_SEARCH_DICT, &CBottomLeft::OnChangeSearchDict)

  ON_NOTIFY(EN_MSGFILTER, IDC_RICHEDIT_BOTTOMLEFT, &CBottomLeft::OnEnMsgfilter)
END_MESSAGE_MAP()


// CBottomLeft message handlers

void CBottomLeft::OnSize(UINT nType, int cx, int cy)
{
	CPropertyPage::OnSize(nType, cx, cy);

	CWnd* pWnd = GetDlgItem(IDC_RICHEDIT_BOTTOMLEFT);

	// resize the rich edit control
	if( ::IsWindow(pWnd ->GetSafeHwnd() )) 
	pWnd ->SetWindowPos(NULL,10,40,cx-200,cy-50,SWP_NOZORDER | SWP_NOACTIVATE);

	// resize the list box
	if( ::IsWindow( EntriesList.GetSafeHwnd() ))
		EntriesList.SetWindowPos(NULL, cx - 180, 70, 170 , cy - 80,  SWP_NOZORDER | SWP_NOACTIVATE);

	// resize the edit box
	if( ::IsWindow( SearchEdit.GetSafeHwnd() ))
		SearchEdit.SetWindowPos(NULL, cx - 180, 40, 170 , 24,  SWP_NOZORDER | SWP_NOACTIVATE);

	



}

BOOL CBottomLeft::OnInitDialog()
{
	CPropertyPage::OnInitDialog();

	if(CSwordBibleDoc::numDictAvailable > 0) 
	{
		active = 0;
		pModule = CSwordBibleDoc::pRTFDictModule[active];
	}

	

	// to receive the mouse click events
	m_lxRich.SetEventMask(ENM_MOUSEEVENTS | ENM_KEYEVENTS | ENM_SCROLLEVENTS);
	m_lxRich.SendMessage(EM_EXLIMITTEXT,0,-1);

	// load the font for displaying the bottom left modules
	font.location = AFX_BOTTOMLEFT;
	font.Load();
	font.ConstructRTF();
	m_lxRich.SetBackgroundColor(FALSE, CColorMgr::RTFtoCOLORREF(font.bg_color) );

    EnableToolTips(TRUE);   // enable tool tips for view

    LoadToolbar();


	// Insert a column in the list control
    EntriesList.InsertColumn(0,"Dictionary" );
	
	// set the width of the column
	CRect listRect;
	EntriesList.GetWindowRect(listRect);
	int width = listRect.Width();
	EntriesList.SetColumnWidth(0, width);

	// Set the item count for the list.If you set the item count to 100 or 1,000,000 doesn't matter, 
	//the time to run this command will still be practically zero. In a non-virtual list, 
	// adding a million elements could take hours.
	numEntries = GetNumEntries();
	EntriesList.SetItemCount(numEntries);

	EntriesList.SetExtendedStyle(EntriesList.GetExtendedStyle() | LVS_EX_FULLROWSELECT);

	
	return FALSE;  // return TRUE unless you set the focus to a control
	// EXCEPTION: OCX Property Pages should return FALSE
}

BOOL CBottomLeft::OnSetActive()
{
	
	// select the first item in the list
	EntriesList.SetItemState(0,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
	EntriesList.EnsureVisible(0,FALSE); 
	

	  return CPropertyPage::OnSetActive();

}


void CBottomLeft::LoadToolbar()
{

	if (!m_wndFormatBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD
       | WS_VISIBLE | CBRS_TOP |  CBRS_FLYBY | CBRS_TOOLTIPS) ||
       !m_wndFormatBar.LoadToolBar(IDR_BOTTOMLEFT_TOOLBAR))
   {
      TRACE0("Failed to create toolbar\n");
      return;      // fail to create
   }

   // buttons size: 16
   // bitmaps:
   // enabled - IDB_TOOLBAR_NORMAL
   // hovered - IDB_TOOLBAR_HOT
   // disabled - IDB_TOOLBAR_DISABLED
   // transparent color RGB(255, 0, 255)
   m_wndFormatBar.LoadTCToolBar(24, IDB_BITMAP_BOTTOMLEFT,IDB_BITMAP_BOTTOMLEFT,
                              IDB_BITMAP_BOTTOMLEFT, RGB(255,255,255));
	

	// Position the control bars using RepositionBars. 
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);

	// Expand the size of the Toolbar window so all controls in it would fit
	
	CRect toolBarRect;
	CToolBarCtrl& formatBar = m_wndFormatBar.GetToolBarCtrl();
	formatBar.GetWindowRect(toolBarRect);
	ScreenToClient(toolBarRect);
	toolBarRect.left += 10  ;
	toolBarRect.right += 500  ;
	formatBar.MoveWindow(toolBarRect,TRUE);


}

int CBottomLeft::GetNumEntries()
{
	if(!pModule) return 0;

	// initial guess
	int high = 10000, low =  0, mid = (high + low)/2;

	while(high - low > 1) {

	pModule->setPosition(sword::TOP); 	pModule->increment(mid);

	if(pModule->Error()) high = mid;
	else low = mid;
	
	mid = (high + low)/2; 
	}

	return high;
}


void CBottomLeft::OnBottomleftCopy()
{
	if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;
	
	m_lxRich.Copy();

}

void CBottomLeft::OnSelectAll(void)
{
	// only the active page should handle the message
  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;

  m_lxRich.SetFocus();
  m_lxRich.SetSel(0,-1);

}




LONG CBottomLeft::OnMainFrameCommand(UINT, LONG)
{ 
	
	// only the active page should handle the message
	  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return 0L;


	
	if (ActiveMainFrameCommand == "print")		         OnPrint();
	else if (ActiveMainFrameCommand == "copy")			OnBottomleftCopy();
	else if (ActiveMainFrameCommand == "selectall")     OnSelectAll();
	else if (ActiveMainFrameCommand == "display")       OnBottomleftFont();
	else if (ActiveMainFrameCommand == "zoom")           OnZoom();
	else if (ActiveMainFrameCommand == "previous")		OnPrevious();
	else if (ActiveMainFrameCommand == "next")          OnNext();

	
	return 0L;

}

void CBottomLeft::OnBottomleftFont()
{
	// only the active page should handle the message
  if(     ((CPropertySheet*) GetParent())->GetActivePage()   != this) return;

  CColorMgr colordlg(NULL, &font);
	
  if(colordlg.DoModal() == IDOK){
   m_lxRich.SetBackgroundColor(false, CColorMgr::RTFtoCOLORREF(font.bg_color) );
  }

	
}


BOOL CBottomLeft::OnToolTipNotify(UINT id, NMHDR *pNMHDR,   LRESULT *pResult)
{
   // need to handle both ANSI and UNICODE versions of the message
   TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
   TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
   CString strTipText;
   UINT nID = pNMHDR->idFrom;
   if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
      pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
   {
      // idFrom is actually the HWND of the tool
      nID = ::GetDlgCtrlID((HWND)nID);
   }

   char buf[100];

   if (nID != 0) // will be zero on a separator
   {AfxLoadString(nID,buf,100 ); strTipText.Format("%s",buf);  }

   if (pNMHDR->code == TTN_NEEDTEXTA)
      lstrcpyn(pTTTA->szText, strTipText, sizeof(pTTTA->szText));
   else
      _mbstowcsz(pTTTW->szText, strTipText, sizeof(pTTTW->szText));
   *pResult = 0;

   return TRUE;    // message was handled
}


void CBottomLeft::OnPrint()
{
		// only the active page should handle the message
  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;

	CBibleForm::pControl = (CRichEditCtrl *) GetDlgItem(IDC_RICHEDIT_BOTTOMLEFT);
	AfxGetMainWnd()->SendMessageToDescendants(WM_PRINT_CONTROL, (WPARAM) 1 , 0);

}




void CBottomLeft::OnZoom()
{
	// only the active page should handle the message
  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;


	CSwordBibleDoc::dictZoom = !(CSwordBibleDoc::dictZoom);

	ZoomText();

}

void CBottomLeft::OnUpdateZoom(CCmdUI *pCmdUI)
{

	// only the active page should handle the message
  if(  !( ((CPropertySheet*) GetParent())->GetActivePage()   == this) )  
	  return;


	pCmdUI->SetCheck(CSwordBibleDoc::dictZoom);
	
}

void CBottomLeft::ZoomText()
{

	if(CSwordBibleDoc::dictZoom) m_lxRich.SendMessage(EM_SETZOOM,5,4); //zoom by 1.25
	else m_lxRich.SendMessage(EM_SETZOOM, 0,0);  // turn off zooming

	
}

void CBottomLeft::OnPrevious()
{
	// select the first item in the list
	POSITION pos = EntriesList.GetFirstSelectedItemPosition( );
	int sel = EntriesList.GetNextSelectedItem(pos);

	if(sel < 0) return;

	EntriesList.SetItemState(sel - 1,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
	EntriesList.EnsureVisible(sel -1 ,FALSE); 

}

void CBottomLeft::OnNext()
{
	// select the first item in the list
	POSITION pos = EntriesList.GetFirstSelectedItemPosition( );
	int sel = EntriesList.GetNextSelectedItem(pos);

	if(sel < 0) return;

	EntriesList.SetItemState(sel + 1,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
	EntriesList.EnsureVisible(sel + 1 ,FALSE); 

}


void CBottomLeft::GetDispInfo(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);

	 //Create a pointer to the item
    LV_ITEM* pItem= &(pDispInfo)->item;

    //Which item number?
    int itemid = pItem->iItem;

    //Do the list need text information?
    if (pItem->mask & LVIF_TEXT)
    {
        CString text;

        //Which column?
        if(pItem->iSubItem == 0)
        {
            //Text is name
			pModule->setPosition(sword::TOP);
			pModule->increment(itemid);
            text = pModule->getKeyText();
        }
    
        //Copy the text to the LV_ITEM structure
        //Maximum number of characters is in pItem->cchTextMax
        lstrcpyn(pItem->pszText, text, pItem->cchTextMax);
    }



	*pResult = 0;
}


// finds an entry in the list Ctrl. Returns index of partial/full match
// Binary search
int CBottomLeft::FindEntry(CString& toFind)
{
	if(!pModule) return 0;

	// initial guess
	high = numEntries, low =  0, mid = (high + low)/2;

	while(high - low > 1) {

	pModule->setPosition(sword::TOP); 	
	pModule->increment(mid);
	KEYText  = pModule->getKeyText();
	
	result = KEYText.CompareNoCase(toFind);
	
	if(result == 0) return mid;
	else if (result > 0)      high = mid;
	else	low = mid;
	
	mid = (high + low)/2; 
	}

	return high;

   


}



void CBottomLeft::OnItemChanged(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	int item = pNMLV->iItem; // item is the zero based index	

	ShowText(item);


	*pResult = 0;
}

void CBottomLeft::ShowText(int sel)
{
	sword::SWKey key =   EntriesList.GetItemText(sel,0);
	pModule->SetKey(key);



	CString chapText = font.GetHeaderRTF();

	CString titleSize;
	titleSize.Format("\\cf2\\fs%d ", font.text_facesize + 5);
	chapText = chapText +  titleSize + (CString) pModule->getKeyText();
		
	// reset the font size
	titleSize.Format("\\fs%d", font.text_facesize);
	chapText = chapText + titleSize + "\\par\\par ";
							
	chapText = chapText +  pModule->RenderText() + "}"; 
							
	chapText.Replace("<a href=\"\">",  "\\cf6\\b ");
	chapText.Replace("</a>", "\\cf2\\b0 ");

	m_lxRich.SetRTF(chapText);

	ZoomText();



}

void CBottomLeft::OnShowVersions()
{
	RECT rc;
    m_wndFormatBar.GetToolBarCtrl().GetItemRect(0,&rc);
	m_wndFormatBar.ClientToScreen(&rc); // TrackPopupMenu uses screen coords

   CMenu menu;
   menu.CreatePopupMenu();
   CMenu* pPopup = NULL;
   pPopup = &menu;

   if(pPopup == NULL) return;
   int dwSelectionMade;  

 
   // fill in the strings for different commentary versions
	for(int i = 0; i < CSwordBibleDoc::numDictAvailable; i++)
		if(CSwordBibleDoc::pRTFDictModule[i] != NULL)
		{
			         pPopup->AppendMenu(MF_STRING,ID_DICTIONARY_NAMES + i, (CString) CSwordBibleDoc::pRTFDictModule[i]->Description());
					 if(active == i) pPopup->CheckMenuItem(ID_DICTIONARY_NAMES + i,MF_CHECKED); 

		}


         dwSelectionMade =  pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON|TPM_RETURNCMD, 
         rc.left, rc.bottom + 4, this);

	
		int selection = dwSelectionMade;
		int base = ID_DICTIONARY_NAMES;
		int temp = selection - base;


	if(temp >= 0 && temp < CSwordBibleDoc::numDictAvailable) 
	{
		 active = temp;
	     pModule = CSwordBibleDoc::pRTFDictModule[active];
	    
		 numEntries = GetNumEntries();
		 EntriesList.SetItemCount(numEntries);

		 EntriesList.SetItemState(1,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);		 
		 EntriesList.SetItemState(0,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
		 
	
	
	}

    pPopup->DestroyMenu(); 


}

void CBottomLeft::OnChangeSearchDict()
{
	SearchEdit.UpdateData(TRUE);
     SearchEdit.GetWindowTextA(search);

	FoundIndex = FindEntry(search);
	 
	EntriesList.SetItemState(FoundIndex,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);
	EntriesList.EnsureVisible(FoundIndex,FALSE); 
}


void CBottomLeft::OnEnMsgfilter(NMHDR *pNMHDR, LRESULT *pResult)
{
	MSGFILTER *pMsgFilter = reinterpret_cast<MSGFILTER *>(pNMHDR);
	
	if (pMsgFilter->msg == WM_RBUTTONDOWN)   OnRightButtonDown();
   
     *pResult = 0; 



}

void CBottomLeft::OnRightButtonDown()
{

	  CPoint point;                                            
     ::GetCursorPos(&point); //where is the mouse?
     CMenu menu; //lets display out context menu :) 
     DWORD dwSelectionMade;                                       
     VERIFY(menu.LoadMenu(IDR_MENU_BOTTOMLEFT) );  
     CMenu *pmenuPopup = menu.GetSubMenu(0);
     ASSERT(pmenuPopup != NULL);   

	 if(CSwordBibleDoc::dictZoom)	pmenuPopup->CheckMenuItem(ID_DICTIONARY_ZOOMTEXT,MF_CHECKED); 

     dwSelectionMade = pmenuPopup->TrackPopupMenu( (TPM_LEFTALIGN|TPM_LEFTBUTTON|
                                                      TPM_RETURNCMD),
                                                    point.x, point.y, this); 

	

	
	switch (dwSelectionMade)
	{
	case ID_DICTIONARY_PRINT:		   OnPrint(); break;
	case ID_DICTIONARY_COPY:			   OnBottomleftCopy(); break;
	case ID_DICTIONARY_SELECTALL:       OnSelectAll(); break;
	case ID_DICTIONARY_DISPLAY:		   OnBottomleftFont(); break;    
	case ID_DICTIONARY_ZOOMTEXT:        OnZoom(); break; 
    case ID_DICTIONARY_PREVIOUSENTRY: OnPrevious(); break;
	case ID_DICTIONARY_NEXTENTRY:     OnNext(); break;

	}

	
	
	pmenuPopup->DestroyMenu(); 



}

